package com.code.config;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.extension.api.R;
import com.code.constant.LoginConstant;
import com.code.filter.TokenCheckFilter;
import com.code.model.Result;
import com.code.service.LoginLogService;
import com.code.service.impl.UserDetailsServiceImpl;
import com.code.service.impl.UserServiceImpl;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import java.io.PrintWriter;
import java.time.Duration;
import java.util.UUID;

@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Autowired
    private UserDetailsServiceImpl userDetailsService;

    @Autowired
    private LoginLogService loginLogService;

    @Autowired
    private TokenCheckFilter tokenCheckFilter;

    /**
     * 关于认证
     *
     * @param auth
     * @throws Exception
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService);
    }

    /**
     * web资源 忽略MVC规则等配置
     *
     * @param web
     * @throws Exception
     */
    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers(
                "/swagger-ui/*",
                "/swagger-resources/**",
                "/v2/api-docs",
                "/v3/api-docs",
                "/webjars/**"
        );
    }

    /**
     * 关于网络请求
     *
     * @param http
     * @throws Exception
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.addFilterBefore(tokenCheckFilter, UsernamePasswordAuthenticationFilter.class);
        http.csrf().disable();
        http.formLogin().loginProcessingUrl("/login").successHandler(successHandler()).failureHandler(failureHandler()).permitAll();
        http.authorizeRequests().antMatchers(LoginConstant.ALLOW_URL).permitAll();
        http.authorizeRequests().anyRequest().authenticated();
    }

    /**
     * 登录失败的处理
     * 1.设置编码
     * 2.处理日志
     * 3.返回信息
     *
     * @return
     */
    private AuthenticationFailureHandler failureHandler() {
        return ((request, response, e) -> {
            response.setContentType("application/json;charset=utf-8");
            loginLogService.addLog(0, MyThreadLocal.threadLocal.get());
            Result<Object> result = new Result<>();
            result.setMsg("账户或密码不正确");
            result.setCode(401);
            ObjectMapper objectMapper = new ObjectMapper();
            String s = objectMapper.writeValueAsString(result);
            PrintWriter writer = response.getWriter();
            writer.write(s);
            writer.flush();
            writer.close();
        });
    }

    /**
     * 登录成功的处理
     * 1.设置编码
     * 2.生产token
     * 3.token存入redis
     * 4.将token写出去
     *
     * @return
     */
    private AuthenticationSuccessHandler successHandler() {
        return ((request, response, authentication) -> {
            response.setContentType("application/json;charset=utf-8");
            String token = UUID.randomUUID().toString().replace("-", "");
            // 存入redis
            redisTemplate.opsForValue().set(LoginConstant.LOGIN_PREFIX + token, JSON.toJSONString(authentication), Duration.ofDays(7));
            // 登录需要记录日志
            loginLogService.addLog(1, MyThreadLocal.threadLocal.get());
            Result<String> result = Result.success("OK", token);
            ObjectMapper objectMapper = new ObjectMapper();
            String s = objectMapper.writeValueAsString(result);
            PrintWriter writer = response.getWriter();
            writer.write(s);
            writer.flush();
            writer.close();
        });
    }

    /**
     * 密码加密
     *
     * @return
     */
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

}
